package articleservice

import (
	"fmt"
	"time"
	"yunj/app/data"
	"yunj/app/enum/articleenum"
	"yunj/app/es/index"
	"yunj/app/service"
	"yunj/pkg/global"
	"yunj/pkg/yunj/core/builder"
	"yunj/pkg/yunj/core/builder/builderconsts"
	"yunj/pkg/yunj/core/builder/builderdto"
	"yunj/pkg/yunj/core/builder/builderenum"
	"yunj/pkg/yunj/core/builder/form"
	"yunj/pkg/yunj/core/builder/form/field"
	"yunj/pkg/yunj/core/builder/table"
	"yunj/pkg/yunj/core/builder/table/col"
	"yunj/pkg/yunj/core/builder/table/col/action"
	"yunj/pkg/yunj/core/builder/table/deftoolbar"
	"yunj/pkg/yunj/core/builder/table/toolbar"
	"yunj/pkg/yunj/core/enum/errcode"
	"yunj/pkg/yunj/util"

	"yunj/app/dto"
	"yunj/app/model"
	"yunj/app/validator/client/articlevalidator"
	"yunj/pkg/yunj/app/enum/stateenum"

	"github.com/gin-gonic/gin"
	"github.com/worklz/go-validate"
	"gorm.io/gorm"
)

type articlePage struct {
}

var ArticlePage *articlePage

// 列表构建器
func (s *articlePage) ListBuilder(ctx *gin.Context) *builder.Table {
	return builder.NewTable(ctx, "ArticleList").
		State([]builderdto.TableState{
			{Code: stateenum.NORMAL, Title: "正常"},
			{Code: stateenum.RECYLE_BIN, Title: "回收站"},
		}).
		Filter(func(t *builder.Table, state *builderdto.TableState) (fields []form.FieldInterface, err error) {
			fields = []form.FieldInterface{
				field.NewText("title", "标题"),
			}
			return
		}).
		Toolbar(func(t *builder.Table, state *builderdto.TableState) (actions []toolbar.ActionInterface, err error) {
			actions = []toolbar.ActionInterface{
				toolbar.NewOpenPopup("add", "添加").SetClass("layui-icon-add-circle").
					SetUrl(util.BuildAdminUrl("/article/list/add")).SetAuth("yunj_article_list_add"),
			}
			if state.Code == stateenum.RECYLE_BIN {
				actions = append(actions,
					toolbar.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_NORMAL, "恢复正常").SetDropdown(true).SetAuth("yunj_article_list_normal"),
					toolbar.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_DELETED, "永久删除").SetDropdown(true).SetAuth("yunj_article_list_deleted"),
				)
			} else {
				actions = append(actions,
					toolbar.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_RECYLE_BIN, "移入回收站").SetDropdown(true).SetAuth("yunj_article_list_recyle_bin"),
				)
			}
			return
		}).
		DefaultToolbar(func(t *builder.Table, state *builderdto.TableState) (actions []deftoolbar.ActionInterface, err error) {
			actions = []deftoolbar.ActionInterface{
				deftoolbar.NewFilter(),
				deftoolbar.NewExport(),
				deftoolbar.NewAsyncEvent("syncES").SetTitle("同步ES").SetClass("yunj-icon-submit").SetAuth("yunj_article_list_sync_es").
					SetConfirmFormFields(field.NewTxt("sync_es_tips", "勾选则同步勾选的文章，反之同步全部文章。<br>确认同步数据至Elasticsearch？")),
			}
			if state.Code == stateenum.NORMAL {
				actions = append(actions,
					deftoolbar.NewAsyncEvent("sendBaiduSitemap").SetTitle("百度收录推送").SetClass("yunj-icon-baidu").SetAuth("yunj_article_list_send_baidu_sitemap").
						SetConfirmFormFields(field.NewTxt("send_baidu_sitemap_tips", "勾选则推送勾选的文章，反之推送全部文章。<br>确认推送文章至百度收录？")),
				)
			}
			return
		}).
		Cols(func(t *builder.Table, state *builderdto.TableState) (cols []table.ColInterface, err error) {
			cols = []table.ColInterface{
				col.NewCheckbox("id", "ID"),
				col.NewImage("cover", "封面").SetAlign(builderconsts.ALIGN_CENTER),
				col.NewText("title", "标题"),
				col.NewText("keywords", "关键词"),
				col.NewText("view_count", "浏览量").SetAlign(builderconsts.ALIGN_CENTER),
				col.NewEnum("status", "发布状态").SetOptions(builder.GetEnumTableColOptions(articleenum.StatusEnum)).SetAlign(builderconsts.ALIGN_CENTER),
				col.NewDatetime("create_time", "创建时间"),
				col.NewDatetime("last_update_time", "上次修改时间"),
			}
			actionCol := col.NewAction("操作").SetOptions([]action.ActionInterface{
				action.NewOpenPopup("edit", "详情").SetClass("layui-icon-survey").SetUrl(util.BuildAdminUrl("/article/list/edit")).SetAuth("yunj_article_list_edit"),
			})
			if state.Code == stateenum.RECYLE_BIN {
				actionCol.Options = append(actionCol.Options,
					action.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_NORMAL, "恢复正常").SetDropdown(true).SetAuth("yunj_article_list_normal"),
					action.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_DELETED, "永久删除").SetDropdown(true).SetAuth("yunj_article_list_deleted").SetConfirmFormFields(field.NewTxt("tips", "确认删除？")),
				)
			} else {
				actionCol.Options = append(actionCol.Options,
					action.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_RECYLE_BIN, "移入回收站").SetDropdown(true).SetAuth("yunj_article_list_recyle_bin"),
				)
			}
			cols = append(cols, actionCol)
			return
		}).
		Validator(func(t *builder.Table) (validate.ValidatorInterface, error) {
			v := &articlevalidator.Article{}
			v.InitValidator(v)
			return v, nil
		}).
		Count(func(t *builder.Table, requestParam *builderdto.TableRequestParam) (res builderdto.TableCountResult, err error) {
			// 状态数量查询
			query := global.MySQL.Model(&model.Article{})
			var filterFormParam dto.AdminArticlePageListFilterFormParam
			util.MapToStruct(requestParam.FilterData(), &filterFormParam)
			s.listBuilderFilterQuery(query, requestParam, &filterFormParam)
			err = query.Count(&res.Count).Error
			if err != nil {
				err = fmt.Errorf("数据量查询异常！%v", err)
				return
			}
			return
		}).
		Items(func(t *builder.Table, requestParam *builderdto.TableRequestParam) (res builderdto.TableItemsResult, err error) {
			query := global.MySQL.Select("*")
			var filterFormParam dto.AdminArticlePageListFilterFormParam
			util.MapToStruct(requestParam.FilterData(), &filterFormParam)
			s.listBuilderFilterQuery(query, requestParam, &filterFormParam)
			var dbItems []*model.Article
			if err = query.Order("create_time desc").Find(&dbItems).Error; err != nil {
				err = fmt.Errorf("数据查询异常！%v", err)
				return
			}
			res.Items, err = s.listBuilderItemsHandle(dbItems)
			return
		}).
		Event(func(t *builder.Table, requestParam *builderdto.TableRequestParam) (res builderdto.TableEventResult, err error) {
			// 移入回收站、恢复正常、删除处理
			handleEventRes, err := builderenum.HandleEvent(ctx, &model.Article{}, requestParam.EventCode, requestParam.Pks, func(param *builderenum.EventDbParam, e *builderenum.Event, pks []any) (err error) {
				if _, exists := param.Data["updated_at"]; exists {
					param.Data["last_update_time"] = time.Now().In(time.Local).Unix()
					delete(param.Data, "updated_at")
				}
				return
			})
			if err != nil {
				return
			}
			if handleEventRes != nil {
				eventCode, ok := builderenum.EventEnum.IsValue(requestParam.EventCode)
				if handleEventRes.Errcode == errcode.SUCCESS && ok {
					if requestParam.EventCodeEqual(builderenum.TABLE_EVENT_TYPE_NORMAL) || requestParam.EventCodeEqual(builderenum.TABLE_EVENT_TYPE_RECYLE_BIN) {
						esErr := service.Article.SyncES(requestParam.Uint64Pks())
						if esErr != nil {
							err = fmt.Errorf("文章%s成功，同步ES失败！%v", builderenum.EventByValue(eventCode).GetDesc(), esErr)
							return
						}
					} else if requestParam.EventCodeEqual(builderenum.TABLE_EVENT_TYPE_DELETED) {
						esErr := index.NewArticle().DocBatchDelete(requestParam.StringPks())
						if esErr != nil {
							err = fmt.Errorf("文章删除成功！ES删除异常：%v", esErr)
							return
						}
					}
				}
				res.ResponseJsonData = handleEventRes
				return
			}
			// 同步ES
			if requestParam.EventCodeEqual("syncES") {
				err = service.Article.SyncES(requestParam.Uint64Pks())
				return
			}
			// 推送百度收录
			if requestParam.EventCodeEqual("sendBaiduSitemap") {
				err = service.Article.SendBaiduSitemap(requestParam.Uint64Pks())
				return
			}
			err = fmt.Errorf("异常事件[%v]", requestParam.EventCode)
			return
		})
}

// 列表数据项处理
func (s *articlePage) listBuilderItemsHandle(items []*model.Article) (resItems []*dto.AdminArticlePageListItemResponse, err error) {
	if len(items) <= 0 {
		return
	}
	// 补充封面图
	service.Article.ItemsAppendCover(items)
	resItems = make([]*dto.AdminArticlePageListItemResponse, 0, len(items))
	for _, item := range items {
		resItem := &dto.AdminArticlePageListItemResponse{
			Id:             item.Id,
			Cover:          item.Cover,
			Title:          item.Title,
			Keywords:       item.Keywords,
			ViewCount:      item.ViewCount,
			Status:         item.Status,
			CreateTime:     item.CreateTime,
			LastUpdateTime: item.LastUpdateTime,
		}
		resItems = append(resItems, resItem)
	}
	return
}

// 列表过滤条件查询对象
func (s *articlePage) listBuilderFilterQuery(query *gorm.DB, requestParam *builderdto.TableRequestParam, filterFormParam *dto.AdminArticlePageListFilterFormParam) {
	// ids
	if len(requestParam.Pks) > 0 {
		query.Where("`id` in ?", requestParam.Pks)
	}
	// state
	stateWhere := stateenum.NORMAL
	if stateCode, ok := stateenum.IsValue(requestParam.State); ok {
		stateWhere = stateCode
	}
	query.Where("state = ?", stateWhere)
	// title
	if filterFormParam.Title != "" {
		keywordsWhere := "%" + filterFormParam.Title + "%"
		query.Where("`title` like ?", keywordsWhere)
	}
}

// 表单id
const FORM_ID = "ArticleForm"

// 表单字段
func (s *articlePage) FormFields(ctx *gin.Context, isEdit bool) (fields []form.FieldInterface, err error) {
	fields = []form.FieldInterface{}

	var article *model.Article
	if isEdit {
		idStr := util.QueryParam(ctx, "id", "")
		if idStr == "" {
			err = fmt.Errorf("异常访问！")
			return
		}
		var id uint64
		id, err = util.ToUint64(idStr)
		if err != nil || id <= 0 {
			err = fmt.Errorf("异常数据访问！")
			return
		}
		article, err = service.Article.ItemById(id)
		if err != nil {
			return
		}
		err = article.AttrAppend()
		if err != nil {
			return
		}
		// id
		idField := field.NewHidden("id").SetVerify("required|positiveInt").SetDefault(id)
		if renderOk, renderErr := idField.RenderHandle(ctx, FORM_ID); renderOk && renderErr == nil {
			fields = append(fields, idField)
		}
	}
	// title
	title := field.NewText("title", "标题").SetVerify("required")
	if isEdit {
		title.SetDefault(article.Title)
	}
	if renderOk, renderErr := title.RenderHandle(ctx, FORM_ID); renderOk && renderErr == nil {
		fields = append(fields, title)
	}
	// content
	content := field.NewMarkdown("content", "内容")
	if isEdit {
		content.SetDefault(article.Content)
	}
	if renderOk, renderErr := content.RenderHandle(ctx, FORM_ID); renderOk && renderErr == nil {
		fields = append(fields, content)
	}
	// category_id
	categoryPageNodes, err := service.Category.TreeNodes(0, nil)
	if err != nil {
		return
	}
	categoryNodes := make([]*builderdto.FormTreeCheckNode, 0, len(categoryPageNodes))
	for _, node := range categoryPageNodes {
		categoryNodes = append(categoryNodes, &builderdto.FormTreeCheckNode{
			Id:   node.Id,
			Name: node.Name,
			Pid:  node.Pid,
		})
	}
	categoryId := field.NewTreeRadio("category_id", "分类").SetNodes(categoryNodes).SetLastChecked(true)
	if isEdit {
		if article.CategoryId == 0 {
			categoryId.SetDefault(data.Category.GetDefaultCategory().Id)
		} else {
			categoryId.SetDefault(article.CategoryId)
		}
	} else {
		categoryId.SetDefault(data.Category.GetDefaultCategory().Id)
	}
	if renderOk, renderErr := categoryId.RenderHandle(ctx, FORM_ID); renderOk && renderErr == nil {
		fields = append(fields, categoryId)
	}
	// keywords
	keywords := field.NewText("keywords", "关键词").SetVerify("commaIntervalChsAlphaNum").SetPlaceholder("关键词。逗号“,”间隔的汉字/字母/数字组合")
	if isEdit {
		keywords.SetDefault(article.Keywords)
	}
	if renderOk, renderErr := keywords.RenderHandle(ctx, FORM_ID); renderOk && renderErr == nil {
		fields = append(fields, keywords)
	}
	// cover
	cover := field.NewImage("cover", "封面")
	if isEdit {
		cover.SetDefault(article.Cover)
	}
	if renderOk, renderErr := cover.RenderHandle(ctx, FORM_ID); renderOk && renderErr == nil {
		fields = append(fields, cover)
	}

	return
}

// 处理新增数据
func (s *articlePage) HandleAdd(ctx *gin.Context) (id uint64, err error) {
	fields, err := ArticlePage.FormFields(ctx, false)
	if err != nil || len(fields) <= 0 {
		return
	}
	// 获取数据
	datas, err := util.PostParams(ctx)
	if err != nil {
		err = fmt.Errorf("获取数据失败：%s", err)
		return
	}
	// 验证字段数据
	v := &articlevalidator.Article{}
	v.InitValidator(v)
	v.AppendRules(map[string]any{
		"status": "required|in:" + articleenum.StatusEnum.GetCommaSepConstStr(),
	})
	err = builder.CheckFormFields(v, datas, fields, "Add")
	if err != nil {
		return
	}
	// 新增处理
	err = service.Article.Add(v.Article, v.ArticleAttr)
	if err != nil {
		return
	}
	id = v.Article.Id
	return
}

// 处理修改数据
func (s *articlePage) HandleEdit(ctx *gin.Context) (err error) {
	fields, err := ArticlePage.FormFields(ctx, true)
	if err != nil || len(fields) <= 0 {
		return
	}
	// 获取数据
	datas, err := util.PostParams(ctx)
	if err != nil {
		err = fmt.Errorf("获取数据失败：%s", err)
		return
	}
	// 验证字段数据
	v := &articlevalidator.Article{}
	v.InitValidator(v)
	v.AppendRules(map[string]any{
		"status": "required|in:" + articleenum.StatusEnum.GetCommaSepConstStr(),
	})
	err = builder.CheckFormFields(v, datas, fields, "Edit")
	if err != nil {
		return
	}
	// 新增处理
	err = service.Article.Update(v.Article, v.ArticleAttr)
	return
}
